home *** CD-ROM | disk | FTP | other *** search
/ Experimental BBS Explossion 3 / Experimental BBS Explossion III.iso / c / tde31.zip / ED.C < prev    next >
C/C++ Source or Header  |  1993-08-29  |  69KB  |  2,277 lines

  1. /*******************  start of original comments  ********************/
  2. /*
  3.  * Written by Douglas Thomson (1989/1990)
  4.  *
  5.  * This source code is released into the public domain.
  6.  */
  7.  
  8. /*
  9.  * Name:    dte - Doug's Text Editor program - main editor module
  10.  * Purpose: This file contains the main editor module, and a number of the
  11.  *           smaller miscellaneous editing commands.
  12.  *          It also contains the code for dispatching commands.
  13.  * File:    ed.c
  14.  * Author:  Douglas Thomson
  15.  * System:  this file is intended to be system-independent
  16.  * Date:    October 1, 1989
  17.  * I/O:     file being edited
  18.  *          files read or written
  19.  *          user commands and prompts
  20.  * Notes:   see the file "dte.doc" for general program documentation
  21.  */
  22. /*********************  end of original comments   ********************/
  23.  
  24. /*
  25.  * The basic editor routines have been EXTENSIVELY rewritten.  I have added
  26.  * support for lines longer than 80 columns and I have added line number
  27.  * support.  I like to know the real line number that editor functions are
  28.  * working on and I like to know the total number of lines in a file.
  29.  *
  30.  * I rewrote the big series of ifs in the dispatch subroutine.  It is now
  31.  * an array of pointers to functions.  We know what function to call as soon
  32.  * as a key is pressed.  It is also makes it easier to implement a configuration
  33.  * utility and macros.
  34.  *
  35.  * I added a few functions that I use quite often and I deleted a few that I
  36.  * rarely use.  Added are Split Line, Join Line, and Duplicate Line.  Deleted
  37.  * are Goto Marker 0-9 (others?).
  38.  *
  39.  * ************ In TDE 1.3, I put Goto Marker 0-9 back in.  ***************
  40.  *
  41.  * I felt that the insert routine should be separated into two routines.  One
  42.  * for inserting the various combinations of newlines and one for inserting
  43.  * ASCII and extended ASCII characters.
  44.  *
  45.  * One of Doug's design considerations was keeping screen updates to a minimum.
  46.  * I have expanded upon that idea and added support for updating windows
  47.  * LOCALly, GLOBALly, or NOT_LOCALly.  For example, scrolling in one window
  48.  * does not affect the text in another window - LOCAL update.  Adding, deleting,
  49.  * or modifying text in one window may affect text in other windows - GLOBAL
  50.  * update.  Sometimes, updates to the current window are handled in the task
  51.  * routines so updates to other windows are done NOT_LOCALly.
  52.  *
  53.  * In version 2.2, the big text buffer scheme was replaced by a double
  54.  *  linked list.
  55.  *
  56.  * New editor name:  TDE, the Thomson-Davis Editor.
  57.  * Author:           Frank Davis
  58.  * Date:             June 5, 1991, version 1.0
  59.  * Date:             July 29, 1991, version 1.1
  60.  * Date:             October 5, 1991, version 1.2
  61.  * Date:             January 20, 1992, version 1.3
  62.  * Date:             February 17, 1992, version 1.4
  63.  * Date:             April 1, 1992, version 1.5
  64.  * Date:             June 5, 1992, version 2.0
  65.  * Date:             October 31, 1992, version 2.1
  66.  * Date:             April 1, 1993, version 2.2
  67.  * Date:             June 5, 1993, version 3.0
  68.  * Date:             August 29, 1993, version 3.1
  69.  *
  70.  * This modification of Douglas Thomson's code is released into the
  71.  * public domain, Frank Davis.   You may distribute it freely.
  72.  */
  73.  
  74. #include "tdestr.h"     /* typedefs for global variables */
  75. #include "define.h"     /* editor function defs */
  76. #include "tdefunc.h"    /* prototypes for all functions in tde */
  77. #include "global.h"     /* global variables */
  78. #include "prompts.h"    /* prompt assignments */
  79. #include "default.h"    /* default function key assignments */
  80.  
  81.  
  82. /*
  83.  * Name:    insert_newline
  84.  * Purpose: insert a newline
  85.  * Date:    June 5, 1991
  86.  * Passed:  window:  pointer to current window
  87.  * Notes:   There a several ways to insert a line into a file:  1) pressing
  88.  *          a key, 2) word wrap, 3) any others?
  89.  *          When doing word wrap or format paragraph, don't show any changes.
  90.  *            Wait until the function finishes then show all changes at once.
  91.  */
  92. int  insert_newline( WINDOW *window )
  93. {
  94. char *source;           /* source for block move to make room for c */
  95. char *dest;             /* destination for block move */
  96. int  len;               /* length of current line */
  97. int  split_len;
  98. int  add;               /* characters to be added (usually 1 in insert mode) */
  99. int  rcol;
  100. int  rc;
  101. long length;
  102. int  carriage_return;
  103. int  split_line;
  104. int  wordwrap;
  105. int  dirty;
  106. int  old_bcol;
  107. register WINDOW *win;   /* put window pointer in a register */
  108. file_infos *file;       /* pointer to file structure in current window */
  109. line_list_ptr new_node;
  110. text_ptr new_line;      /* new line */
  111.  
  112.    rc = OK;
  113.    win = window;
  114.    file = win->file_info;
  115.    length = file->length;
  116.    wordwrap = mode.word_wrap;
  117.    switch (g_status.command) {
  118.       case WordWrap :
  119.          carriage_return = TRUE;
  120.          split_line = FALSE;
  121.          break;
  122.       case AddLine  :
  123.          split_line = carriage_return = FALSE;
  124.          break;
  125.       case SplitLine :
  126.          split_line = carriage_return = TRUE;
  127.          break;
  128.       case Rturn :
  129.       default    :
  130.  
  131.          /*
  132.           * if file is opened in BINARY mode, lets keep the user from
  133.           *   unintentially inserting a line feed into the text.
  134.           */
  135.          if (file->crlf == BINARY)
  136.             return( next_line( win ) );
  137.  
  138.          show_ruler_char( win );
  139.          carriage_return = TRUE;
  140.          split_line = FALSE;
  141.          break;
  142.    }
  143.  
  144.    /*
  145.     * make window temporarily invisible to the un_copy_line function
  146.     */
  147.    new_node = (line_list_ptr)my_malloc( sizeof(line_list_struc), &rc );
  148.    new_line = NULL;
  149.    win->visible = FALSE;
  150.    old_bcol = win->bcol;
  151.    if (rc == OK) {
  152.       new_node->line  = new_line;
  153.       new_node->len   = 0;
  154.       new_node->dirty = FALSE;
  155.  
  156.       if (win->ll->len != EOF) {
  157.          win->file_info->modified = TRUE;
  158.          if (mode.do_backups == TRUE)
  159.             rc = backup_file( win );
  160.          copy_line( win->ll );
  161.          detab_linebuff( );
  162.          len = g_status.line_buff_len;
  163.          split_len = 0;
  164.          if (win->rcol < len)
  165.             win->ll->dirty = TRUE;
  166.  
  167.          source = g_status.line_buff + len;
  168.          if (carriage_return || split_line) {
  169.             if (win->rcol < len) {
  170.                source = g_status.line_buff + win->rcol;
  171.                split_len = len - win->rcol;
  172.                len = win->rcol;
  173.             }
  174.          }
  175.          g_status.line_buff_len = len;
  176.          entab_linebuff( );
  177.          if (un_copy_line( win->ll, win, TRUE ) == OK) {
  178.  
  179.             assert( split_len >= 0 );
  180.             assert( split_len < MAX_LINE_LENGTH );
  181.  
  182.             memmove( g_status.line_buff, source, split_len );
  183.             g_status.line_buff_len = len = split_len;
  184.             g_status.copied = TRUE;
  185.             entab_linebuff( );
  186.          } else
  187.             rc = ERROR;
  188.       } else {
  189.          g_status.line_buff_len = len = 0;
  190.          g_status.copied = TRUE;
  191.       }
  192.  
  193.       if (rc == OK) {
  194.          new_node->line  = new_line;
  195.          new_node->len   = 0;
  196.          new_node->dirty = TRUE;
  197.  
  198.          /*
  199.           * we are somewhere in the list and we need to insert the new node.
  200.           *  if we are anywhere except the EOF node, insert the new node
  201.           *  after the current node.  if the current node is the EOF node,
  202.           *  insert the new node before the EOF node.  this keeps the
  203.           *  EOF node at the end of the list.
  204.           */
  205.          if (win->ll->next != NULL) {
  206.             win->ll->next->prev = new_node;
  207.             new_node->next = win->ll->next;
  208.             win->ll->next = new_node;
  209.             new_node->prev = win->ll;
  210.          } else {
  211.             new_node->next = win->ll;
  212.             if (win->ll->prev != NULL)
  213.                win->ll->prev->next = new_node;
  214.             new_node->prev = win->ll->prev;
  215.             win->ll->prev = new_node;
  216.             if (new_node->prev == NULL)
  217.                win->file_info->line_list = new_node;
  218.             win->ll = new_node;
  219.          }
  220.  
  221.          ++file->length;
  222.          detab_linebuff( );
  223.          entab_linebuff( );
  224.          rc = un_copy_line( new_node, win, FALSE );
  225.          adjust_windows_cursor( win, 1 );
  226.  
  227.          file->dirty = NOT_LOCAL;
  228.          if (length == 0l || wordwrap || win->cline == win->bottom_line)
  229.             file->dirty = GLOBAL;
  230.          else if (!split_line)
  231.             update_line( win );
  232.  
  233.          /*
  234.           * If the cursor is to move down to the next line, then update
  235.           *  the line and column appropriately.
  236.           */
  237.          if (rc == OK  &&  (carriage_return || split_line)) {
  238.             dirty = file->dirty;
  239.             if (win->cline < win->bottom_line)
  240.                win->cline++;
  241.             win->rline++;
  242.             if (win->ll->next != NULL) {
  243.                win->bin_offset += win->ll->len;
  244.                win->ll = win->ll->next;
  245.             }
  246.             rcol = win->rcol;
  247.             old_bcol = win->bcol;
  248.  
  249.             if (win->ll->next != NULL) {
  250.                if (mode.indent || wordwrap) {
  251.                   /*
  252.                    * autoindentation is required. Match the indentation of
  253.                    *  the first line above that is not blank.
  254.                    */
  255.                   add = find_left_margin( wordwrap == FIXED_WRAP ?
  256.                                           win->ll : win->ll->prev, wordwrap );
  257.  
  258.                   assert( add >= 0 );
  259.                   assert( add < MAX_LINE_LENGTH );
  260.  
  261.                   copy_line( win->ll );
  262.                   detab_linebuff( );
  263.                   len = g_status.line_buff_len;
  264.                   source = g_status.line_buff;
  265.                   if (len + add > MAX_LINE_LENGTH)
  266.                      add = MAX_LINE_LENGTH - len;
  267.                   dest = source + add;
  268.  
  269.                   assert( len >= 0);
  270.                   assert( len < MAX_LINE_LENGTH );
  271.  
  272.                   memmove( dest, source, len );
  273.  
  274.                   /*
  275.                    * now put in the autoindent characters
  276.                    */
  277.  
  278.                   assert( add >= 0 );
  279.                   assert( add < MAX_LINE_LENGTH );
  280.  
  281.                   memset( source, ' ', add );
  282.                   win->rcol = add;
  283.                   g_status.line_buff_len += add;
  284.                   entab_linebuff( );
  285.                   rc = un_copy_line( win->ll, win, TRUE );
  286.                } else
  287.                   win->rcol = 0;
  288.             }
  289.             if (rc == OK  &&  split_line) {
  290.                win->rline--;
  291.                win->ll = win->ll->prev;
  292.                if (win->cline > win->top_line + window->ruler)
  293.                   win->cline--;
  294.                win->rcol = rcol;
  295.             }
  296.             check_virtual_col( win, win->rcol, win->ccol );
  297.             if (dirty == GLOBAL || file->dirty == LOCAL || wordwrap)
  298.                file->dirty = GLOBAL;
  299.             else
  300.                file->dirty = dirty;
  301.          }
  302.       } else {
  303.          if (new_node != NULL)
  304.             my_free( new_node );
  305.       }
  306.    } else {
  307.       if (new_node != NULL)
  308.          my_free( new_node );
  309.       error( WARNING, window->bottom_line, main4 );
  310.    }
  311.  
  312.    /*
  313.     * record that file has been modified
  314.     */
  315.    win->visible = TRUE;
  316.    if (rc == OK) {
  317.       if (file->dirty != GLOBAL)
  318.          my_scroll_down( win );
  319.       restore_marked_block( win, 1 );
  320.       show_size( win );
  321.       show_avail_mem( );
  322.       if (old_bcol != win->bcol) {
  323.          make_ruler( win );
  324.          show_ruler( win );
  325.       }
  326.    }
  327.    return( rc );
  328. }
  329.  
  330.  
  331. /*
  332.  * Name:    insert_overwrite
  333.  * Purpose: To make the necessary changes after the user has typed a normal
  334.  *           printable character
  335.  * Date:    June 5, 1991
  336.  * Passed:  window:  pointer to current window
  337.  */
  338. int  insert_overwrite( WINDOW *window )
  339. {
  340. char *source;           /* source for block move to make room for c */
  341. char *dest;             /* destination for block move */
  342. int  len;               /* length of current line */
  343. int  pad;               /* padding to add if cursor beyond end of line */
  344. int  add;               /* characters to be added (usually 1 in insert mode) */
  345. register int rcol;
  346. register WINDOW *win;  /* put window pointer in a register */
  347. int  rc;
  348.  
  349.    win = window;
  350.    if (win->ll->len == EOF || g_status.key_pressed >= 256)
  351.       rc = OK;
  352.    else {
  353.       rcol = win->rcol;
  354.       /*
  355.        * first check we have room - the editor can not
  356.        *  cope with lines wider than g_display.line_length
  357.        */
  358.       if (rcol >= g_display.line_length) {
  359.          /*
  360.           * cannot insert more characters
  361.           */
  362.          error( WARNING, win->bottom_line, ed2 );
  363.          rc = ERROR;
  364.       } else {
  365.          copy_line( win->ll );
  366.          detab_linebuff( );
  367.  
  368.          /*
  369.           * work out how many characters need to be inserted
  370.           */
  371.          len = g_status.line_buff_len;
  372.          pad = rcol > len ? rcol - len : 0;
  373.  
  374.          if (mode.insert || rcol >= len)
  375.             /*
  376.              * inserted characters, or overwritten characters at the end of
  377.              *  the line, are inserted.
  378.              */
  379.             add = 1;
  380.          else
  381.             /*
  382.              *  and no extra space is required to overwrite existing characters
  383.              */
  384.             add = 0;
  385.  
  386.          /*
  387.           * check that current line would not get too long.
  388.           */
  389.          if (len + pad + add >= g_display.line_length) {
  390.             /*
  391.              * no more room to add
  392.              */
  393.             error( WARNING, win->bottom_line, ed3 );
  394.             rc = ERROR;
  395.          } else {
  396.  
  397.             /*
  398.              * make room for whatever needs to be inserted
  399.              */
  400.             if (pad > 0  || add > 0) {
  401.                source = g_status.line_buff + rcol - pad;
  402.                dest = source + pad + add;
  403.  
  404.                assert( len + pad - rcol >= 0 );
  405.                assert( len + pad - rcol < MAX_LINE_LENGTH );
  406.  
  407.                memmove( dest, source, len + pad - rcol );
  408.  
  409.                /*
  410.                 * put in the required padding
  411.                 */
  412.  
  413.                assert( pad >= 0 );
  414.                assert( pad < MAX_LINE_LENGTH );
  415.  
  416.                memset( source, ' ', pad );
  417.             }
  418.             g_status.line_buff[rcol] = (char)g_status.key_pressed;
  419.             g_status.line_buff_len += pad + add;
  420.             entab_linebuff( );
  421.  
  422.             /*
  423.              * always increment the real column (rcol) then adjust the
  424.              * logical and base column as needed.   show the changed line
  425.              * in all but the LOCAL window.  In the LOCAL window, there are
  426.              * two cases:  1) update the line, or 2) redraw the window if
  427.              * cursor goes too far right.
  428.              */
  429.             win->file_info->dirty = NOT_LOCAL;
  430.             win->ll->dirty = TRUE;
  431.             show_changed_line( win );
  432.             if (win->ccol < win->end_col) {
  433.                show_curl_line( win );
  434.                show_ruler_char( win );
  435.                win->ccol++;
  436.             } else {
  437.                win->bcol++;
  438.                win->file_info->dirty = LOCAL;
  439.                make_ruler( win );
  440.                show_ruler( win );
  441.             }
  442.             rcol++;
  443.          }
  444.  
  445.          /*
  446.           * record that file has been modified and adjust cursors,
  447.           * file start and end pointers as needed.
  448.           */
  449.          check_virtual_col( win, rcol, win->ccol );
  450.          win->file_info->modified = TRUE;
  451.          if (mode.word_wrap) {
  452.             add = mode.right_justify;
  453.             mode.right_justify = FALSE;
  454.             g_status.command = FormatText;
  455.             word_wrap( win );
  456.             mode.right_justify = add;
  457.          }
  458.          rc = OK;
  459.       }
  460.    }
  461.    return( rc );
  462. }
  463.  
  464.  
  465. /*
  466.  * Name:    join_line
  467.  * Purpose: To join current line and line below at cursor
  468.  * Date:    June 5, 1991
  469.  * Passed:  window:  pointer to current window
  470.  * Notes:   trunc the line.  then, join with line below, if it exists.
  471.  */
  472. int  join_line( WINDOW *window )
  473. {
  474. int  len;               /* length of current line */
  475. int  new_len;           /* length of the joined lines */
  476. int  next_len;          /* length of the line below current line */
  477. text_ptr q;             /* next line in file */
  478. text_ptr tab_free;      /* next line in file -- with the tabs removed */
  479. int  pad;               /* padding spaces required */
  480. register WINDOW *win;   /* put window pointer in a register */
  481. WINDOW *wp;
  482. line_list_ptr next_node;
  483. int  rc;
  484.  
  485.    win = window;
  486.    if (win->ll->len == EOF  ||  win->ll->next->len == EOF)
  487.       return( ERROR );
  488.  
  489.    rc = OK;
  490.  
  491.    assert( win->ll->next != NULL );
  492.  
  493.    next_node = win->ll->next;
  494.    load_undo_buffer( win->file_info, win->ll->line, win->ll->len );
  495.    copy_line( win->ll );
  496.    detab_linebuff( );
  497.  
  498.    /*
  499.     * if cursor is in line before eol, reset len to rcol
  500.     */
  501.    if (win->rcol < (len = g_status.line_buff_len))
  502.       len = win->rcol;
  503.  
  504.    /*
  505.     * calculate needed padding
  506.     */
  507.    pad = win->rcol > len ? win->rcol - len : 0;
  508.  
  509.    assert( pad >= 0 );
  510.    assert( pad < MAX_LINE_LENGTH );
  511.  
  512.    /*
  513.     * if there any tabs in the next line, expand them because we
  514.     *   probably have to redo them anyway.
  515.     */
  516.    next_len = next_node->len;
  517.    tab_free = detab_a_line( next_node->line, &next_len );
  518.  
  519.    assert( next_len >= 0 );
  520.    assert( next_len < MAX_LINE_LENGTH );
  521.    assert( len >= 0 );
  522.    assert( len < MAX_LINE_LENGTH );
  523.  
  524.    /*
  525.     * check room to combine lines
  526.     */
  527.    new_len = len + pad + next_len;
  528.    if (new_len >= g_display.line_length) {
  529.       /*
  530.        * cannot combine lines.
  531.        */
  532.       error( WARNING, win->bottom_line, ed4 );
  533.       rc = ERROR;
  534.    } else {
  535.       if (mode.do_backups == TRUE) {
  536.          win->file_info->modified = TRUE;
  537.          rc = backup_file( win );
  538.       }
  539.       q = (text_ptr)(g_status.line_buff + len);
  540.       /*
  541.        * insert padding
  542.        */
  543.       if (pad > 0) {
  544.          while (pad--)
  545.             *q++ = ' ';
  546.       }
  547.       _fmemcpy( q, tab_free, next_len );
  548.       g_status.line_buff_len = new_len;
  549.       entab_linebuff( );
  550.  
  551.       if ((rc = un_copy_line( win->ll, win, FALSE )) == OK) {
  552.  
  553.          if (next_node->next != NULL)
  554.             next_node->next->prev = win->ll;
  555.          win->ll->next = next_node->next;
  556.          win->ll->dirty = TRUE;
  557.  
  558.          --win->file_info->length;
  559.          ++win->rline;
  560.          adjust_windows_cursor( win, -1 );
  561.          restore_marked_block( win, -1 );
  562.          --win->rline;
  563.  
  564.          wp = g_status.window_list;
  565.          while (wp != NULL) {
  566.             if (wp->file_info == win->file_info) {
  567.                /*
  568.                 * make sure none of the window pointers point to the
  569.                 *   node we are about to delete.
  570.                 */
  571.                if (wp != win) {
  572.                   if (wp->ll == next_node)
  573.                      wp->ll = win->ll->next;
  574.                }
  575.             }
  576.             wp = wp->next;
  577.          }
  578.  
  579.          /*
  580.           * now, it's safe to delete the next_node line as well as
  581.           *   the next node.
  582.           */
  583.          if (next_node->line != NULL)
  584.             my_free( next_node->line );
  585.          my_free( next_node );
  586.  
  587.          show_size( win );
  588.          show_avail_mem( );
  589.          win->file_info->dirty = GLOBAL;
  590.       }
  591.    }
  592.    return( rc );
  593. }
  594.  
  595.  
  596. /*
  597.  * Name:    word_delete
  598.  * Purpose: To delete from the cursor to the start of the next word.
  599.  * Date:    September 1, 1991
  600.  * Passed:  window:  pointer to current window
  601.  * Notes:   If the cursor is at the right of the line, then combine the
  602.  *           current line with the next one, leaving the cursor where it
  603.  *           is.
  604.  *          If the cursor is on an alphanumeric character, then all
  605.  *           subsequent alphanumeric characters are deleted.
  606.  *          If the cursor is on a space, then all subsequent spaces
  607.  *           are deleted.
  608.  *          If the cursor is on a punctuation character, then all
  609.  *           subsequent punctuation characters are deleted.
  610.  */
  611. int  word_delete( WINDOW *window )
  612. {
  613. int  len;               /* length of current line */
  614. int  count;             /* number of characters deleted from line */
  615. register int start;     /* column that next word starts in */
  616. char *source;           /* source for block move to delete word */
  617. char *dest;             /* destination for block move */
  618. text_ptr p;
  619. register WINDOW *win;   /* put window pointer in a register */
  620. int  rc;
  621.  
  622.    win = window;
  623.    if (win->rline > win->file_info->length  || win->ll->len == EOF)
  624.       return( ERROR );
  625.  
  626.    rc = OK;
  627.    copy_line( win->ll );
  628.    detab_linebuff( );
  629.    if (win->rcol >= (len = g_status.line_buff_len)) {
  630.       rc = join_line( win );
  631.       if (rc == OK) {
  632.          p = win->ll->line;
  633.          if (p != NULL) {
  634.             p += win->rcol;
  635.             if (win->rcol < win->ll->len) {
  636.                len = win->ll->len - win->rcol;
  637.                load_undo_buffer( win->file_info, p, len );
  638.             }
  639.          }
  640.       }
  641.    } else {
  642.  
  643.       assert( len >= 0);
  644.       assert( len < MAX_LINE_LENGTH );
  645.  
  646.       /*
  647.        * normal word delete
  648.        *
  649.        * find the start of the next word
  650.        */
  651.       start = win->rcol;
  652.       if (isspace( g_status.line_buff[start] )) {
  653.          /*
  654.           * the cursor was on a space, so eat all consecutive spaces
  655.           *  from the cursor onwards.
  656.           */
  657.          while (start < len  &&  isspace( g_status.line_buff[start] ))
  658.             ++start;
  659.       } else {
  660.          /*
  661.           * eat all consecutive characters in the same class (spaces
  662.           *  are considered to be in the same class as the cursor
  663.           *  character)
  664.           */
  665.          while (start < len  &&  !isspace( g_status.line_buff[start] ))
  666.             ++start;
  667.          while (start < len  &&  isspace( g_status.line_buff[start] ))
  668.             ++start;
  669.       }
  670.  
  671.       /*
  672.        * move text to delete word
  673.        */
  674.       count = start - win->rcol;
  675.       source = g_status.line_buff + start;
  676.       dest = g_status.line_buff + win->rcol;
  677.  
  678.       assert( len - start >= 0 );
  679.  
  680.       memmove( dest, source, len - start );
  681.       g_status.line_buff_len = len - count;
  682.       entab_linebuff( );
  683.       win->file_info->modified = TRUE;
  684.       win->file_info->dirty = GLOBAL;
  685.       win->ll->dirty = TRUE;
  686.  
  687.       /*
  688.        * word_delete is also called by the word processing functions to get
  689.        *   rid of spaces.
  690.        */
  691.       if (g_status.command == WordDelete)
  692.          show_changed_line( win );
  693.    }
  694.    return( rc );
  695. }
  696.  
  697.  
  698. /*
  699.  * Name:    dup_line
  700.  * Purpose: Duplicate current line
  701.  * Date:    June 5, 1991
  702.  * Passed:  window:  pointer to current window
  703.  * Notes:   cursor stays on current line
  704.  */
  705. int  dup_line( WINDOW *window )
  706. {
  707. register int len;       /* length of current line */
  708. text_ptr p;
  709. register WINDOW *win;   /* put window pointer in a register */
  710. line_list_ptr next_node;
  711. int  rc;
  712.  
  713.    win = window;
  714.  
  715.    /*
  716.     * don't dup a NULL line
  717.     */
  718.    if (win->rline > win->file_info->length  ||  win->ll->len == EOF)
  719.       return( ERROR );
  720.  
  721.    entab_linebuff( );
  722.    rc = un_copy_line( win->ll, win, TRUE );
  723.    len = win->ll->len;
  724.  
  725.    assert( len >= 0);
  726.    assert( len < MAX_LINE_LENGTH );
  727.  
  728.    p = NULL;
  729.    next_node = NULL;
  730.    if (rc == OK) {
  731.       p = (text_ptr)my_malloc( len, &rc );
  732.       next_node = (line_list_ptr)my_malloc( sizeof(line_list_struc), &rc );
  733.    }
  734.  
  735.    if (rc == OK) {
  736.       win->file_info->modified = TRUE;
  737.       if (mode.do_backups == TRUE)
  738.          rc = backup_file( win );
  739.       ++win->file_info->length;
  740.  
  741.       if (len > 0)
  742.          _fmemcpy( p, win->ll->line, len );
  743.  
  744.       next_node->line  = p;
  745.       next_node->dirty = TRUE;
  746.       next_node->len   = len;
  747.  
  748.       if (win->ll->next != NULL)
  749.          win->ll->next->prev = next_node;
  750.  
  751.       next_node->next = win->ll->next;
  752.       next_node->prev = win->ll;
  753.       win->ll->next = next_node;
  754.  
  755.       adjust_windows_cursor( win, 1 );
  756.  
  757.       /*
  758.        * if current line is the bottom line, we can't see the dup line because
  759.        * cursor doesn't move and dup line is added after current line.
  760.        */
  761.       if  (win->cline != win->bottom_line)
  762.          my_scroll_down( win );
  763.       win->file_info->dirty = NOT_LOCAL;
  764.  
  765.       /*
  766.        * record that file has been modified
  767.        */
  768.       restore_marked_block( win, 1 );
  769.       show_size( win );
  770.       show_avail_mem( );
  771.    } else {
  772.       /*
  773.        * cannot duplicate line
  774.        */
  775.       if (p != NULL)
  776.          my_free( p );
  777.       if (next_node != NULL)
  778.          my_free( next_node );
  779.       error( WARNING, win->bottom_line, ed5 );
  780.    }
  781.    return( rc );
  782. }
  783.  
  784.  
  785. /*
  786.  * Name:    back_space
  787.  * Purpose: To delete the character to the left of the cursor.
  788.  * Date:    June 5, 1991
  789.  * Passed:  window:  pointer to current window
  790.  * Notes:   If the cursor is at the left of the line, then combine the
  791.  *           current line with the previous one.
  792.  *          If in indent mode, and the cursor is on the first non-blank
  793.  *           character of the line, then match the indentation of an
  794.  *           earlier line.
  795.  */
  796. int  back_space( WINDOW *window )
  797. {
  798. int  rc;                /* return code */
  799. int  len;               /* length of the current line */
  800. char *source;           /* source of block move to delete character */
  801. char *dest;             /* destination of block move */
  802. text_ptr p;             /* previous line in file */
  803. int  plen;              /* length of previous line */
  804. int  del_count;         /* number of characters to delete */
  805. int  pos;               /* the position of the first non-blank char */
  806. register int rcol;
  807. int  ccol;
  808. int  old_bcol;
  809. register WINDOW *win;  /* put window pointer in a register */
  810. WINDOW *wp;
  811. line_list_ptr temp_ll;
  812.  
  813.    win = window;
  814.    if (win->rline > win->file_info->length || win->ll->len == EOF)
  815.       return( ERROR );
  816.    rc = OK;
  817.    copy_line( win->ll );
  818.    detab_linebuff( );
  819.    len = g_status.line_buff_len;
  820.    rcol = win->rcol;
  821.    ccol = win->ccol;
  822.    old_bcol = win->bcol;
  823.    if (rcol == 0) {
  824.       if (win->rline > 1) {
  825.          /*
  826.           * combine this line with the previous, if any
  827.           */
  828.  
  829.          assert( win->ll->prev != NULL );
  830.  
  831.          p = win->ll->prev->line;
  832.          plen = win->ll->prev->len;
  833.          if (len + 2 + plen >= g_display.line_length) {
  834.             /*
  835.              * cannot combine lines
  836.              */
  837.             error( WARNING, win->bottom_line, ed4 );
  838.             return( ERROR );
  839.          }
  840.  
  841.          win->file_info->modified = TRUE;
  842.          if ((rc = un_copy_line( win->ll, win, TRUE )) == OK) {
  843.             --win->rline;
  844.             win->ll = win->ll->prev;
  845.             win->bin_offset -= win->ll->len;
  846.             win->ll->dirty = TRUE;
  847.             copy_line( win->ll );
  848.             detab_linebuff( );
  849.             len = g_status.line_buff_len;
  850.             rcol = len;
  851.  
  852.             p = win->ll->next->line;
  853.             plen = win->ll->next->len;
  854.  
  855.             /*
  856.              * copy previous line into new previous line.
  857.              */
  858.             assert( plen >= 0 );
  859.             assert( len  >= 0 );
  860.  
  861.             _fmemcpy( g_status.line_buff+len, p, plen );
  862.             g_status.line_buff_len = len + plen;
  863.  
  864.             load_undo_buffer( win->file_info, p, plen );
  865.             if (p != NULL)
  866.                my_free( p );
  867.  
  868.             temp_ll = win->ll->next;
  869.  
  870.             if (temp_ll->prev != NULL)
  871.                temp_ll->prev->next = temp_ll->next;
  872.             temp_ll->next->prev = temp_ll->prev;
  873.  
  874.             --win->file_info->length;
  875.             ++win->rline;
  876.             restore_marked_block( win, -1 );
  877.             adjust_windows_cursor( win, -1 );
  878.             --win->rline;
  879.  
  880.             wp = g_status.window_list;
  881.             while (wp != NULL) {
  882.                if (wp->file_info == win->file_info) {
  883.                   if (wp != win) {
  884.                      if (wp->ll == temp_ll)
  885.                         wp->ll = win->ll->next;
  886.                   }
  887.                }
  888.                wp = wp->next;
  889.             }
  890.  
  891.             my_free( temp_ll );
  892.  
  893.             if (win->cline > win->top_line + win->ruler)
  894.                --win->cline;
  895.  
  896.             /*
  897.              * make sure cursor stays on the screen, at the end of the
  898.              *  previous line
  899.              */
  900.             ccol = rcol - win->bcol;
  901.             show_size( win );
  902.             show_avail_mem( );
  903.             check_virtual_col( win, rcol, ccol );
  904.             win->file_info->dirty = GLOBAL;
  905.             make_ruler( win );
  906.             show_ruler( win );
  907.          }
  908.       } else
  909.          return( ERROR );
  910.    } else {
  911.       /*
  912.        * normal delete
  913.        *
  914.        * find out how much to delete (depends on indent mode)
  915.        */
  916.       del_count = 1;   /* the default */
  917.       if (mode.indent) {
  918.          /*
  919.           * indent only happens if the cursor is on the first
  920.           *  non-blank character of the line
  921.           */
  922.          pos = first_non_blank( (text_ptr)g_status.line_buff, len );
  923.          if (pos == rcol  ||
  924.                          is_line_blank( (text_ptr)g_status.line_buff, len )) {
  925.             /*
  926.              * now work out how much to indent
  927.              */
  928.             temp_ll = win->ll->prev;
  929.             for (; temp_ll != NULL; temp_ll=temp_ll->prev) {
  930.                p = temp_ll->line;
  931.                plen = first_non_blank( p, temp_ll->len );
  932.                if (plen < rcol  &&  plen != temp_ll->len) {
  933.                   /*
  934.                    * found the line to match
  935.                    */
  936.                   del_count = rcol - plen;
  937.                   break;
  938.                }
  939.             }
  940.          }
  941.       }
  942.  
  943.       /*
  944.        * move text to delete char(s), unless no chars actually there
  945.        */
  946.       if (rcol - del_count < len) {
  947.          dest = g_status.line_buff + rcol - del_count;
  948.          if (rcol > len) {
  949.             source = g_status.line_buff + len;
  950.             pos = 0;
  951.             len = (rcol + 1) - del_count;
  952.          } else {
  953.             source = g_status.line_buff + rcol;
  954.             pos = len - rcol;
  955.             len = len - del_count;
  956.          }
  957.  
  958.          assert( pos >= 0 );
  959.          assert( len >= 0 );
  960.          assert( len <= MAX_LINE_LENGTH );
  961.  
  962.          memmove( dest, source, pos );
  963.          g_status.line_buff_len = len;
  964.          entab_linebuff( );
  965.       }
  966.       rcol -= del_count;
  967.       ccol -= del_count;
  968.       win->file_info->dirty = NOT_LOCAL;
  969.       win->ll->dirty = TRUE;
  970.       show_ruler_char( win );
  971.       show_changed_line( win );
  972.       check_virtual_col( win, rcol, ccol );
  973.       if (!win->file_info->dirty)
  974.          show_curl_line( win );
  975.       if (old_bcol != win->bcol) {
  976.          make_ruler( win );
  977.          show_ruler( win );
  978.       }
  979.    }
  980.    win->file_info->modified = TRUE;
  981.    return( rc );
  982. }
  983.  
  984.  
  985. /*
  986.  * Name:    line_kill
  987.  * Purpose: To delete the line the cursor is on.
  988.  * Date:    June 5, 1991
  989.  * Passed:  window:  pointer to current window
  990.  * Notes:   win->ll->s == NULL then do not do a
  991.  *          line kill (can't kill a NULL line).
  992.  */
  993. int  line_kill( WINDOW *window )
  994. {
  995. register WINDOW *win;   /* put window pointer in a register */
  996. register WINDOW *wp;
  997. line_list_ptr killed_node;
  998. int  rc;
  999.  
  1000.    win = window;
  1001.    killed_node = win->ll;
  1002.    rc = OK;
  1003.    if (killed_node->len != EOF) {
  1004.       win->file_info->modified = TRUE;
  1005.       if (mode.do_backups == TRUE)
  1006.          rc = backup_file( win );
  1007.  
  1008.       if (rc == OK) {
  1009.          load_undo_buffer( win->file_info,
  1010.             g_status.copied ? (text_ptr)g_status.line_buff : killed_node->line,
  1011.             g_status.copied ? g_status.line_buff_len       : killed_node->len );
  1012.  
  1013.          --win->file_info->length;
  1014.  
  1015.          win->ll = win->ll->next;
  1016.  
  1017.          if (killed_node->prev != NULL)
  1018.             killed_node->prev->next = killed_node->next;
  1019.          else
  1020.             win->file_info->line_list = win->ll;
  1021.  
  1022.          killed_node->next->prev = killed_node->prev;
  1023.  
  1024.          wp = g_status.window_list;
  1025.          while (wp != NULL) {
  1026.             if (wp->file_info == win->file_info) {
  1027.                if (wp != win) {
  1028.                   if (wp->ll == killed_node)
  1029.                      wp->ll = win->ll;
  1030.                }
  1031.             }
  1032.             wp = wp->next;
  1033.          }
  1034.  
  1035.          /*
  1036.           * free the line and the node
  1037.           */
  1038.          if (killed_node->line != NULL)
  1039.             my_free( killed_node->line );
  1040.          my_free( killed_node );
  1041.  
  1042.          win->file_info->dirty = NOT_LOCAL;
  1043.  
  1044.          g_status.copied = FALSE;
  1045.          /*
  1046.           * move all cursors one according to i, restore begin and end block
  1047.           */
  1048.          adjust_windows_cursor( win, -1 );
  1049.          restore_marked_block( win, -1 );
  1050.  
  1051.          /*
  1052.           * we are not doing a GLOBAL update, so update current window here
  1053.           */
  1054.          if (win->file_info->dirty == NOT_LOCAL)
  1055.             my_scroll_down( win );
  1056.          show_size( win );
  1057.          show_avail_mem( );
  1058.       }
  1059.    } else
  1060.       rc = ERROR;
  1061.    return( rc );
  1062. }
  1063.  
  1064.  
  1065. /*
  1066.  * Name:    char_del_under
  1067.  * Purpose: To delete the character under the cursor.
  1068.  * Date:    June 5, 1991
  1069.  * Passed:  window:  pointer to current window
  1070.  * Notes:   If the cursor is beyond the end of the line, then this
  1071.  *           command is ignored.
  1072.  *          DeleteChar and StreamDeleteChar use this function.
  1073.  */
  1074. int  char_del_under( WINDOW *window )
  1075. {
  1076. char *source;    /* source of block move to delete character */
  1077. int  len;
  1078. register WINDOW *win;   /* put window pointer in a register */
  1079.  
  1080.    win = window;
  1081.    if (win->rline > win->file_info->length || win->ll->len == EOF)
  1082.       return( OK );
  1083.    copy_line( win->ll );
  1084.    detab_linebuff( );
  1085.    if (win->rcol < (len = g_status.line_buff_len)) {
  1086.       /*
  1087.        * move text to delete using buffer
  1088.        */
  1089.       source = g_status.line_buff + win->rcol + 1;
  1090.  
  1091.       assert( len - win->rcol >= 0 );
  1092.  
  1093.       memmove( source-1, source, len - win->rcol );
  1094.       --g_status.line_buff_len;
  1095.       entab_linebuff( );
  1096.       win->file_info->dirty    = GLOBAL;
  1097.       win->file_info->modified = TRUE;
  1098.       win->ll->dirty = TRUE;
  1099.       show_changed_line( win );
  1100.    } else if (g_status.command == StreamDeleteChar)
  1101.       join_line( win );
  1102.    return( OK );
  1103. }
  1104.  
  1105.  
  1106. /*
  1107.  * Name:    eol_kill
  1108.  * Purpose: To delete everything from the cursor to the end of the line.
  1109.  * Date:    June 5, 1991
  1110.  * Passed:  window:  pointer to current window
  1111.  * Notes:   If the cursor is beyond the end of the line, then this
  1112.  *           command is ignored.
  1113.  */
  1114. int  eol_kill( WINDOW *window )
  1115. {
  1116. register WINDOW *win;   /* put window pointer in a register */
  1117.  
  1118.    win = window;
  1119.    if (win->rline > win->file_info->length  ||  win->ll->len == EOF)
  1120.       return( OK );
  1121.    copy_line( win->ll );
  1122.    detab_linebuff( );
  1123.    load_undo_buffer( win->file_info, (text_ptr)g_status.line_buff,
  1124.                      g_status.line_buff_len );
  1125.    if (win->rcol < g_status.line_buff_len) {
  1126.       /*
  1127.        * truncate to delete rest of line
  1128.        */
  1129.       g_status.line_buff_len = win->rcol;
  1130.       entab_linebuff( );
  1131.       win->file_info->dirty = GLOBAL;
  1132.       win->ll->dirty = TRUE;
  1133.       show_changed_line( win );
  1134.    }
  1135.    return( OK );
  1136. }
  1137.  
  1138.  
  1139. /*
  1140.  * Name:    undo_line
  1141.  * Purpose: To retrieve unaltered line if possible.
  1142.  * Date:    June 5, 1991
  1143.  * Passed:  window:  pointer to current window
  1144.  * Notes:   Changes are made to the line buffer so the underlying text has
  1145.  *          not changed.  Put the unchanged line from the file into the
  1146.  *          line buffer and display it.
  1147.  */
  1148. int  undo_line( WINDOW *window )
  1149. {
  1150. register WINDOW *win;   /* put window pointer in a register */
  1151.  
  1152.    win = window;
  1153.    if (win->rline <= win->file_info->length  &&  win->ll->len != EOF &&
  1154.                             g_status.copied) {
  1155.       g_status.copied = FALSE;
  1156.       copy_line( win->ll );
  1157.       detab_linebuff( );
  1158.       win->file_info->dirty = GLOBAL;
  1159.       show_changed_line( win );
  1160.    }
  1161.    return( OK );
  1162. }
  1163.  
  1164.  
  1165. /*
  1166.  * Name:    undo
  1167.  * Purpose: To retrieve (pop) a line from the undo stack
  1168.  * Date:    September 26, 1991
  1169.  * Passed:  window:  pointer to current window
  1170.  * Notes:   Insert an empty line into the file then pop the line in the undo
  1171.  *          stack.  When we pop line 0, there are no more lines on the stack.
  1172.  *          Set the stack pointer to -1 to indicate an empty stack.
  1173.  */
  1174. int  undo( WINDOW *window )
  1175. {
  1176. register WINDOW *win;   /* put window pointer in a register */
  1177. line_list_ptr node;
  1178.  
  1179.    win = window;
  1180.    if (win->file_info->undo_count > 0) {
  1181.       entab_linebuff( );
  1182.       if (un_copy_line( win->ll, win, TRUE ) == ERROR)
  1183.          return( ERROR );
  1184.  
  1185.       node = win->file_info->undo_top;
  1186.       win->file_info->undo_top = node->next;
  1187.       win->file_info->undo_top->prev = NULL;
  1188.       --win->file_info->undo_count;
  1189.  
  1190.       node->next = node->prev = NULL;
  1191.  
  1192.       ++win->file_info->length;
  1193.  
  1194.       if (win->ll->prev != NULL)
  1195.          win->ll->prev->next = node;
  1196.       node->prev = win->ll->prev;
  1197.  
  1198.       win->ll->prev = node;
  1199.       node->next = win->ll;
  1200.       win->ll = node;
  1201.       win->ll->dirty = TRUE;
  1202.  
  1203.       if (win->ll->prev == NULL)
  1204.          win->file_info->line_list = win->ll;
  1205.  
  1206.       adjust_windows_cursor( win, 1 );
  1207.  
  1208.       /*
  1209.        * we have now undeleted a line.  increment the file length and display
  1210.         * it.
  1211.        */
  1212.       win->file_info->dirty = GLOBAL;
  1213.       show_size( win );
  1214.       show_avail_mem( );
  1215.    }
  1216.    return( OK );
  1217. }
  1218.  
  1219.  
  1220. /*
  1221.  * Name:    beg_next_line
  1222.  * Purpose: To move the cursor to the beginning of the next line.
  1223.  * Date:    October 4, 1991
  1224.  * Passed:  window:  pointer to current window
  1225.  */
  1226. int  beg_next_line( WINDOW *window )
  1227. {
  1228. int  rc;
  1229.  
  1230.    window->rcol = 0;
  1231.    rc = prepare_move_down( window );
  1232.    check_virtual_col( window, window->rcol, window->ccol );
  1233.    sync( window );
  1234.    make_ruler( window );
  1235.    show_ruler( window );
  1236.    return( rc );
  1237. }
  1238.  
  1239.  
  1240. /*
  1241.  * Name:    next_line
  1242.  * Purpose: To move the cursor to the first character of the next line.
  1243.  * Date:    October 4, 1991
  1244.  * Passed:  window:  pointer to current window
  1245.  */
  1246. int  next_line( WINDOW *window )
  1247. {
  1248. register int rcol;
  1249. register WINDOW *win;   /* put window pointer in a register */
  1250. int  rc;
  1251.  
  1252.    win = window;
  1253.    rc = prepare_move_down( win );
  1254.    rcol = first_non_blank( win->ll->line, win->ll->len );
  1255.    check_virtual_col( win, rcol, win->ccol );
  1256.    sync( win );
  1257.    make_ruler( win );
  1258.    show_ruler( win );
  1259.    return( rc );
  1260. }
  1261.  
  1262.  
  1263. /*
  1264.  * Name:    home
  1265.  * Purpose: To move the cursor to the left of the current line.
  1266.  * Date:    June 5, 1991
  1267.  * Passed:  window:  pointer to current window
  1268.  * Notes:   this routine is made a little more complicated with cursor sync.
  1269.  *            if the g_status.copied flag is set we need to see from what file
  1270.  *            the line_buff was copied.
  1271.  */
  1272. int  home( WINDOW *window )
  1273. {
  1274. register int rcol;
  1275. register WINDOW *win;   /* put window pointer in a register */
  1276. text_ptr p;
  1277.  
  1278.    win = window;
  1279.    if (g_status.copied && win->file_info == g_status.current_window->file_info){
  1280.       rcol = first_non_blank( (text_ptr)g_status.line_buff,
  1281.                                         g_status.line_buff_len );
  1282.       if (is_line_blank( (text_ptr)g_status.line_buff, g_status.line_buff_len))
  1283.          rcol = 0;
  1284.    } else {
  1285.       p = win->ll->line;
  1286.       if (p == NULL)
  1287.          rcol = 0;
  1288.       else {
  1289.          rcol = first_non_blank( p, win->ll->len );
  1290.          if (is_line_blank( p, win->ll->len ))
  1291.             rcol = 0;
  1292.       }
  1293.    }
  1294.    if (win->rcol == rcol)
  1295.       rcol = 0;
  1296.    check_virtual_col( win, rcol, win->ccol );
  1297.    sync( win );
  1298.    make_ruler( win );
  1299.    show_ruler( win );
  1300.    return( OK );
  1301. }
  1302.  
  1303.  
  1304. /*
  1305.  * Name:    goto_eol
  1306.  * Purpose: To move the cursor to the eol character of the current line.
  1307.  * Date:    June 5, 1991
  1308.  * Passed:  window:  pointer to current window
  1309.  * Notes:   this routine is made a little more complicated with cursor sync.
  1310.  *            if the g_status.copied flag is set we need to see from what file
  1311.  *            the line_buff was copied.
  1312.  */
  1313. int  goto_eol( WINDOW *window )
  1314. {
  1315. register int rcol;
  1316. register WINDOW *win;   /* put window pointer in a register */
  1317.  
  1318.    win = window;
  1319.    rcol = find_end( win->ll->line, win->ll->len );
  1320.    if (g_status.copied) {
  1321.       if (win->file_info == g_status.current_window->file_info)
  1322.          rcol = find_end( (text_ptr)g_status.line_buff, g_status.line_buff_len);
  1323.    }
  1324.    win->ccol = win->start_col + rcol - win->bcol;
  1325.    check_virtual_col( win, rcol, win->ccol );
  1326.    sync( win );
  1327.    make_ruler( win );
  1328.    show_ruler( win );
  1329.    return( OK );
  1330. }
  1331.  
  1332.  
  1333. /*
  1334.  * Name:    goto_top
  1335.  * Purpose: To move the cursor to the top of the current window.
  1336.  * Date:    June 5, 1991
  1337.  * Passed:  window:  pointer to current window
  1338.  * Notes:   If the start of the file occurs before the top of the window,
  1339.  *           then the start of the file is moved to the top of the window.
  1340.  */
  1341. int  goto_top( WINDOW *window )
  1342. {
  1343. register WINDOW *win;   /* put window pointer in a register */
  1344.  
  1345.    win = window;
  1346.    entab_linebuff( );
  1347.    if (un_copy_line( win->ll, win, TRUE ) == ERROR)
  1348.       return( ERROR );
  1349.    update_line( win );
  1350.    for (; win->cline > win->top_line+win->ruler; win->cline--,win->rline--) {
  1351.       if (win->rline <= 1L)
  1352.          break;
  1353.       else {
  1354.          win->ll = win->ll->prev;
  1355.          win->bin_offset -= win->ll->len;
  1356.       }
  1357.    }
  1358.    show_curl_line( win );
  1359.    sync( win );
  1360.    return( OK );
  1361. }
  1362.  
  1363.  
  1364. /*
  1365.  * Name:    goto_bottom
  1366.  * Purpose: To move the cursor to the bottom of the current window.
  1367.  * Date:    June 5, 1991
  1368.  * Passed:  window:  pointer to current window
  1369.  */
  1370. int  goto_bottom( WINDOW *window )
  1371. {
  1372. register WINDOW *win;   /* put window pointer in a register */
  1373. int  at_top;
  1374.  
  1375.    win = window;
  1376.    entab_linebuff( );
  1377.    if (un_copy_line( win->ll, win, TRUE ) == ERROR)
  1378.       return( ERROR );
  1379.    if (win->ll->len == EOF) {
  1380.       if (win->rline > 1) {
  1381.          at_top = FALSE;
  1382.          if (win->cline == win->top_line + win->ruler) {
  1383.             win->file_info->dirty = LOCAL;
  1384.             at_top = TRUE;
  1385.          }
  1386.          if (!at_top)
  1387.             update_line( win );
  1388.          --win->rline;             /* ALWAYS decrement line counter */
  1389.          win->ll = win->ll->prev;
  1390.          win->bin_offset -= win->ll->len;
  1391.          if (!at_top) {
  1392.             --win->cline;          /* we aren't at top of screen - so move up */
  1393.             show_curl_line( win );
  1394.          }
  1395.       }
  1396.    } else {
  1397.       update_line( win );
  1398.       for (; win->cline < win->bottom_line; win->cline++,win->rline++) {
  1399.          if (win->ll == NULL || win->ll->next == NULL || win->ll->next->len == EOF)
  1400.             break;
  1401.          else {
  1402.             win->bin_offset += win->ll->len;
  1403.             win->ll = win->ll->next;
  1404.          }
  1405.       }
  1406.       show_curl_line( win );
  1407.    }
  1408.    sync( win );
  1409.    return( OK );
  1410. }
  1411.  
  1412.  
  1413. /*
  1414.  * Name:    set_tabstop
  1415.  * Purpose: To set the current interval between tab stops
  1416.  * Date:    October 1, 1989
  1417.  * Notes:   Tab interval must be reasonable, and this function will
  1418.  *           not allow tabs more than MAX_COLS / 2.
  1419.  */
  1420. int  set_tabstop( WINDOW *window )
  1421. {
  1422. char num_str[MAX_COLS]; /* tab interval as a character string */
  1423. int  tab;               /* new tab interval */
  1424. register int rc;
  1425. register file_infos *file;
  1426.  
  1427.    itoa( mode.ltab_size, num_str, 10 );
  1428.    /*
  1429.     * tab interval:
  1430.     */
  1431.    rc = get_name( ed7a, window->bottom_line, num_str, g_display.message_color );
  1432.    if (rc == OK   &&  *num_str != '\0') {
  1433.       tab = atoi( num_str );
  1434.       if (tab < MAX_COLS/2) {
  1435.          mode.ltab_size = tab;
  1436.          if (mode.inflate_tabs) {
  1437.             for (file=g_status.file_list; file != NULL; file=file->next)
  1438.                file->dirty = GLOBAL;
  1439.          }
  1440.       } else {
  1441.          /*
  1442.           * tab size too long
  1443.           */
  1444.          error( WARNING, window->bottom_line, ed8 );
  1445.          rc = ERROR;
  1446.       }
  1447.    }
  1448.  
  1449.    itoa( mode.ptab_size, num_str, 10 );
  1450.    /*
  1451.     * tab interval:
  1452.     */
  1453.    rc = get_name( ed7b, window->bottom_line, num_str, g_display.message_color );
  1454.    if (rc == OK  &&  *num_str != '\0') {
  1455.       tab = atoi( num_str );
  1456.       if (tab < MAX_COLS/2) {
  1457.          mode.ptab_size = tab;
  1458.          show_tab_modes( );
  1459.          if (mode.inflate_tabs) {
  1460.             for (file=g_status.file_list; file != NULL; file=file->next)
  1461.                file->dirty = GLOBAL;
  1462.          }
  1463.       } else {
  1464.          /*
  1465.           * tab size too long
  1466.           */
  1467.          error( WARNING, window->bottom_line, ed8 );
  1468.          rc = ERROR;
  1469.       }
  1470.    }
  1471.    return( rc );
  1472. }
  1473.  
  1474.  
  1475. /*
  1476.  * Name:    show_line_col
  1477.  * Purpose: show current real line and column of current cursor position
  1478.  * Date:    June 5, 1991
  1479.  * Passed:  window:  pointer to current window
  1480.  * Notes:   Blank old position and display new position.  current line and
  1481.  *          column may take up to 12 columns, which allows the display of
  1482.  *          9,999 columns and 9,999,999 lines.
  1483.  */
  1484. void show_line_col( WINDOW *window )
  1485. {
  1486. int  i;
  1487. register int k;
  1488. char line_col[20], num[10];
  1489. char *hex_digit = "0123456789abcdef";
  1490.  
  1491.    /*
  1492.     * blank out current line:column position.
  1493.     */
  1494.    memset( line_col, ' ', 13 );
  1495.    line_col[13] = '\0';
  1496.  
  1497.    /*
  1498.     * convert column to ascii and store in display buffer.
  1499.     */
  1500.    itoa( window->rcol+1, num, 10 );
  1501.    i = strlen( num ) - 1;
  1502.    for (k=12; i>=0; i--, k--)
  1503.       line_col[k] = num[i];
  1504.  
  1505.    /*
  1506.     * put in colon to separate line and column
  1507.     */
  1508.    line_col[k--] = ':';
  1509.  
  1510.    /*
  1511.     * convert line to ascii and store in display buffer.
  1512.     */
  1513.    ltoa( window->rline, num, 10 );
  1514.    i = strlen( num ) - 1;
  1515.    for (; i>=0; i--, k--)
  1516.       line_col[k] = num[i];
  1517.  
  1518.    /*
  1519.     * find line to start line:column display then output
  1520.     */
  1521.    s_output( line_col, window->top_line-1, window->end_col-12,
  1522.              g_display.head_color );
  1523.  
  1524.    strcpy( line_col, " =   " );
  1525.    i = window->rcol;
  1526.    if (g_status.copied) {
  1527.       if (mode.inflate_tabs)
  1528.          i = entab_adjust_rcol( (text_ptr)g_status.line_buff,
  1529.                                  g_status.line_buff_len, i );
  1530.       if (i < g_status.line_buff_len) {
  1531.          k = (int)g_status.line_buff[i];
  1532.          line_col[2] = *(hex_digit + (k >> 4));
  1533.          line_col[3] = *(hex_digit + (k & 0x000f));
  1534.          line_col[4] = 'x';
  1535.          i = TRUE;
  1536.       } else
  1537.          i = FALSE;
  1538.    } else {
  1539.       if (mode.inflate_tabs  &&  window->ll->len != EOF)
  1540.          i = entab_adjust_rcol( window->ll->line, window->ll->len, i );
  1541.       if (i < window->ll->len) {
  1542.          k = (int)window->ll->line[i];
  1543.          line_col[2] = *(hex_digit + (k >> 4));
  1544.          line_col[3] = *(hex_digit + (k & 0x000f));
  1545.          line_col[4] = 'x';
  1546.          i = TRUE;
  1547.       } else
  1548.          i = FALSE;
  1549.    }
  1550.    s_output( line_col, g_display.mode_line, 58, g_display.mode_color );
  1551.    if (i == TRUE)
  1552.       c_output( k, 58, g_display.mode_line, g_display.mode_color );
  1553.  
  1554.  
  1555.    /*
  1556.     * if file was opened in binary mode, show offset from beginning of file.
  1557.     */
  1558.    if (window->file_info->crlf == BINARY && !window->vertical) {
  1559.       k =  window->ll->line == NULL  ?  0  :  window->rcol;
  1560.       memset( line_col, ' ', 7 );
  1561.       line_col[7] = '\0';
  1562.       s_output( line_col, window->top_line-1, 61, g_display.head_color );
  1563.       ltoa( window->bin_offset + k, line_col, 10 );
  1564.       s_output( line_col, window->top_line-1, 61, g_display.head_color );
  1565.    }
  1566.    show_asterisk( window );
  1567. }
  1568.  
  1569.  
  1570. /*
  1571.  * Name:    show_asterisk
  1572.  * Purpose: give user an indication if file is dirty
  1573.  * Date:    September 16, 1991
  1574.  * Passed:  window:  pointer to current window
  1575.  */
  1576. void show_asterisk( WINDOW *window )
  1577. {
  1578.    c_output( window->file_info->modified ? '*' : ' ', window->start_col+4,
  1579.              window->top_line-1, g_display.head_color );
  1580. }
  1581.  
  1582.  
  1583. /*
  1584.  * Name:    toggle_overwrite
  1585.  * Purpose: toggle overwrite-insert mode
  1586.  * Date:    September 16, 1991
  1587.  * Passed:  arg_filler:  argument to satify function prototype
  1588.  */
  1589. int  toggle_overwrite( WINDOW *arg_filler )
  1590. {
  1591.    mode.insert = !mode.insert;
  1592.    show_insert_mode( );
  1593.    set_cursor_size( mode.insert ? g_display.insert_cursor :
  1594.                     g_display.overw_cursor );
  1595.    return( OK );
  1596. }
  1597.  
  1598.  
  1599. /*
  1600.  * Name:    toggle_smart_tabs
  1601.  * Purpose: toggle smart tab mode
  1602.  * Date:    June 5, 1992
  1603.  * Passed:  arg_filler:  argument to satify function prototype
  1604.  */
  1605. int  toggle_smart_tabs( WINDOW *arg_filler )
  1606. {
  1607.    mode.smart_tab = !mode.smart_tab;
  1608.    show_tab_modes( );
  1609.    return( OK );
  1610. }
  1611.  
  1612.  
  1613. /*
  1614.  * Name:    toggle_indent
  1615.  * Purpose: toggle indent mode
  1616.  * Date:    September 16, 1991
  1617.  * Passed:  arg_filler:  argument to satify function prototype
  1618.  */
  1619. int  toggle_indent( WINDOW *arg_filler )
  1620. {
  1621.    mode.indent = !mode.indent;
  1622.    show_indent_mode( );
  1623.    return( OK );
  1624. }
  1625.  
  1626.  
  1627. /*
  1628.  * Name:    set_left_margin
  1629.  * Purpose: set left margin for word wrap
  1630.  * Date:    November 27, 1991
  1631.  * Passed:  window
  1632.  */
  1633. int  set_left_margin( WINDOW *window )
  1634. {
  1635. register int rc;
  1636. char temp[MAX_COLS];
  1637.  
  1638.    itoa( mode.left_margin + 1, temp, 10 );
  1639.    /*
  1640.     * enter left margin
  1641.     */
  1642.    rc = get_name( ed9, window->bottom_line, temp, g_display.message_color );
  1643.    if (rc == OK  &&  *temp != '\0') {
  1644.       rc = atoi( temp ) - 1;
  1645.       if (rc < 0 || rc >= mode.right_margin) {
  1646.          /*
  1647.           * left margin out of range
  1648.           */
  1649.          error( WARNING, window->bottom_line, ed10 );
  1650.          rc = ERROR;
  1651.       } else {
  1652.          mode.left_margin = rc;
  1653.          show_all_rulers( );
  1654.       }
  1655.    }
  1656.    return( rc );
  1657. }
  1658.  
  1659.  
  1660. /*
  1661.  * Name:    set_right_margin
  1662.  * Purpose: set right margin for word wrap
  1663.  * Date:    November 27, 1991
  1664.  * Passed:  window
  1665.  */
  1666. int  set_right_margin( WINDOW *window )
  1667. {
  1668. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  1669. register int rc;
  1670. int  prompt_line;
  1671. char temp[MAX_COLS];
  1672.  
  1673.    prompt_line = window->bottom_line;
  1674.    save_screen_line( 0, prompt_line, line_buff );
  1675.    set_prompt( ed11a, prompt_line );
  1676.    rc = get_yn( );
  1677.    restore_screen_line( 0, prompt_line, line_buff );
  1678.    if (rc != ERROR) {
  1679.       mode.right_justify =  rc == A_YES ? TRUE : FALSE;
  1680.  
  1681.       itoa( mode.right_margin + 1, temp, 10 );
  1682.       /*
  1683.        * enter right margin
  1684.        */
  1685.       rc = get_name( ed11, prompt_line, temp, g_display.message_color );
  1686.       if (rc == OK  &&  *temp != '\0') {
  1687.          rc = atoi( temp ) - 1;
  1688.          if (rc <= mode.left_margin || rc > MAX_LINE_LENGTH) {
  1689.             /*
  1690.              * right margin out of range
  1691.              */
  1692.             error( WARNING, prompt_line, ed12 );
  1693.             rc = ERROR;
  1694.          } else {
  1695.             mode.right_margin = rc;
  1696.             show_all_rulers( );
  1697.          }
  1698.       }
  1699.    }
  1700.    return( rc );
  1701. }
  1702.  
  1703.  
  1704. /*
  1705.  * Name:    set_paragraph_margin
  1706.  * Purpose: set column to begin paragraph
  1707.  * Date:    November 27, 1991
  1708.  * Passed:  window
  1709.  * Notes:   paragraph may be indented, flush, or offset.
  1710.  */
  1711. int  set_paragraph_margin( WINDOW *window )
  1712. {
  1713. register int rc;
  1714. char temp[80];
  1715.  
  1716.    itoa( mode.parg_margin + 1, temp, 10 );
  1717.    /*
  1718.     * enter paragraph margin
  1719.     */
  1720.    rc = get_name( ed13, window->bottom_line, temp, g_display.message_color );
  1721.    if (rc == OK  &&  *temp != '\0') {
  1722.       rc = atoi( temp ) - 1;
  1723.       if (rc < 0 || rc >= mode.right_margin) {
  1724.          /*
  1725.           * paragraph margin out of range
  1726.           */
  1727.          error( WARNING, window->bottom_line, ed14 );
  1728.          rc = ERROR;
  1729.       } else {
  1730.          mode.parg_margin = rc;
  1731.          show_all_rulers( );
  1732.       }
  1733.    }
  1734.    return( rc );
  1735. }
  1736.  
  1737.  
  1738. /*
  1739.  * Name:    toggle_crlf
  1740.  * Purpose: toggle crlf mode
  1741.  * Date:    November 27, 1991
  1742.  * Passed:  arg_filler:  argument to satify function prototype
  1743.  */
  1744. int  toggle_crlf( WINDOW *window )
  1745. {
  1746. register WINDOW *w;
  1747.  
  1748.    ++window->file_info->crlf;
  1749.    if (window->file_info->crlf > BINARY)
  1750.       window->file_info->crlf = CRLF;
  1751.    w = g_status.window_list;
  1752.    while (w != NULL) {
  1753.       if (w->file_info == window->file_info  &&  w->visible)
  1754.          show_crlf_mode( w );
  1755.       w = w->next;
  1756.    }
  1757.    return( OK );
  1758. }
  1759.  
  1760.  
  1761. /*
  1762.  * Name:    toggle_ww
  1763.  * Purpose: toggle word wrap mode
  1764.  * Date:    November 27, 1991
  1765.  * Passed:  arg_filler:  argument to satify function prototype
  1766.  */
  1767. int  toggle_ww( WINDOW *arg_filler )
  1768. {
  1769.    ++mode.word_wrap;
  1770.    if (mode.word_wrap > DYNAMIC_WRAP)
  1771.       mode.word_wrap = NO_WRAP;
  1772.    show_wordwrap_mode( );
  1773.    return( OK );
  1774. }
  1775.  
  1776.  
  1777. /*
  1778.  * Name:    toggle_trailing
  1779.  * Purpose: toggle eleminating trainling space at eol
  1780.  * Date:    November 25, 1991
  1781.  * Passed:  arg_filler:  argument to satify function prototype
  1782.  */
  1783. int  toggle_trailing( WINDOW *arg_filler )
  1784. {
  1785.    mode.trailing = !mode.trailing;
  1786.    show_trailing( );
  1787.    return( OK );
  1788. }
  1789.  
  1790.  
  1791. /*
  1792.  * Name:    toggle_z
  1793.  * Purpose: toggle writing control z at eof
  1794.  * Date:    November 25, 1991
  1795.  * Passed:  arg_filler:  argument to satify function prototype
  1796.  */
  1797. int  toggle_z( WINDOW *arg_filler )
  1798. {
  1799.    mode.control_z = !mode.control_z;
  1800.    show_control_z( );
  1801.    return( OK );
  1802. }
  1803.  
  1804.  
  1805. /*
  1806.  * Name:    toggle_eol
  1807.  * Purpose: toggle writing eol character at eol
  1808.  * Date:    November 25, 1991
  1809.  * Passed:  arg_filler:  argument to satify function prototype
  1810.  */
  1811. int  toggle_eol( WINDOW *arg_filler )
  1812. {
  1813. register file_infos *file;
  1814.  
  1815.    mode.show_eol = !mode.show_eol;
  1816.    for (file=g_status.file_list; file != NULL; file=file->next)
  1817.       file->dirty = GLOBAL;
  1818.    return( OK );
  1819. }
  1820.  
  1821.  
  1822. /*
  1823.  * Name:    toggle_search_case
  1824.  * Purpose: toggle search case
  1825.  * Date:    September 16, 1991
  1826.  * Passed:  arg_filler:  argument to satify function prototype
  1827.  */
  1828. int  toggle_search_case( WINDOW *arg_filler )
  1829. {
  1830.    mode.search_case = (mode.search_case == IGNORE) ? MATCH : IGNORE;
  1831.    show_search_case( );
  1832.    build_boyer_array( );
  1833.    return( OK );
  1834. }
  1835.  
  1836.  
  1837. /*
  1838.  * Name:    toggle_sync
  1839.  * Purpose: toggle sync mode
  1840.  * Date:    January 15, 1992
  1841.  * Passed:  arg_filler:  argument to satify function prototype
  1842.  */
  1843. int  toggle_sync( WINDOW *arg_filler )
  1844. {
  1845.    mode.sync = !mode.sync;
  1846.    show_sync_mode( );
  1847.    return( OK );
  1848. }
  1849.  
  1850.  
  1851. /*
  1852.  * Name:    toggle_ruler
  1853.  * Purpose: toggle ruler
  1854.  * Date:    March 5, 1992
  1855.  * Passed:  arg_filler:  argument to satify function prototype
  1856.  */
  1857. int  toggle_ruler( WINDOW *arg_filler )
  1858. {
  1859. register WINDOW *wp;
  1860.  
  1861.    mode.ruler = !mode.ruler;
  1862.    wp = g_status.window_list;
  1863.    while (wp != NULL) {
  1864.       if (mode.ruler) {
  1865.          /*
  1866.           * there has to be more than one line in a window to display a ruler.
  1867.           *   even if the ruler mode is on, we need to check the num of lines.
  1868.           */
  1869.          if (wp->bottom_line - wp->top_line >0) {
  1870.             if (wp->cline == wp->top_line)
  1871.                ++wp->cline;
  1872.             if (wp->cline > wp->bottom_line)
  1873.                wp->cline = wp->bottom_line;
  1874.             wp->ruler = TRUE;
  1875.          } else
  1876.             wp->ruler = FALSE;
  1877.       } else {
  1878.  
  1879.          /*
  1880.           * if this is the first page in a file, then we may need to "pull"
  1881.           *   the file up before displaying the first page.
  1882.           */
  1883.          if (wp->rline == ((wp->cline - wp->ruler) - (wp->top_line - 1)))
  1884.             --wp->cline;
  1885.          if (wp->cline < wp->top_line)
  1886.             wp->cline = wp->top_line;
  1887.          wp->ruler = FALSE;
  1888.       }
  1889.       make_ruler( wp );
  1890.       setup_window( wp );
  1891.       if (wp->visible)
  1892.          redraw_current_window( wp );
  1893.       wp = wp->next;
  1894.    }
  1895.    return( OK );
  1896. }
  1897.  
  1898.  
  1899. /*
  1900.  * Name:    toggle_tabinflate
  1901.  * Purpose: toggle inflating tabs
  1902.  * Date:    October 31, 1992
  1903.  * Passed:  arg_filler:  argument to satify function prototype
  1904.  */
  1905. int  toggle_tabinflate( WINDOW *arg_filler )
  1906. {
  1907. register file_infos *file;
  1908.  
  1909.    mode.inflate_tabs = !mode.inflate_tabs;
  1910.    for (file=g_status.file_list; file != NULL; file=file->next)
  1911.       file->dirty = GLOBAL;
  1912.    show_tab_modes( );
  1913.    return( OK );
  1914. }
  1915.  
  1916.  
  1917. /*
  1918.  * Name:    sync
  1919.  * Purpose: carry out cursor movements in all visible windows
  1920.  * Date:    January 15, 1992
  1921.  * Passed:  window
  1922.  * Notes:   switch sync semaphore when we do this so we don't get into a
  1923.  *          recursive loop.  all cursor movement commands un_copy_line before
  1924.  *          moving the cursor off the current line.   you MUST make certain
  1925.  *          that the current line is uncopied in the task routines that
  1926.  *          move the cursor off the current line before calling sync.
  1927.  */
  1928. void sync( WINDOW *window )
  1929. {
  1930. register WINDOW *wp;
  1931. register file_infos *fp;
  1932.  
  1933.    if (mode.sync && mode.sync_sem) {
  1934.  
  1935.    /*
  1936.     * these functions must un_copy a line before sync'ing
  1937.     */
  1938. #if defined( __MSC__ )
  1939.       switch (g_status.command) {
  1940.          case  NextLine        :
  1941.          case  BegNextLine     :
  1942.          case  LineDown        :
  1943.          case  LineUp          :
  1944.          case  WordRight       :
  1945.          case  WordLeft        :
  1946.          case  ScreenDown      :
  1947.          case  ScreenUp        :
  1948.          case  EndOfFile       :
  1949.          case  TopOfFile       :
  1950.          case  BotOfScreen     :
  1951.          case  TopOfScreen     :
  1952.          case  JumpToLine      :
  1953.          case  CenterWindow    :
  1954.          case  CenterLine      :
  1955.          case  ScrollDnLine    :
  1956.          case  ScrollUpLine    :
  1957.          case  PanUp           :
  1958.          case  PanDn           :
  1959.          case  NextDirtyLine   :
  1960.          case  PrevDirtyLine   :
  1961.          case  ParenBalance    :
  1962.             assert( g_status.copied == FALSE );
  1963.             break;
  1964.          default  :
  1965.             break;
  1966.       }
  1967. #endif
  1968.  
  1969.       mode.sync_sem = FALSE;
  1970.       for (wp = g_status.window_list;  wp != NULL;  wp = wp->next) {
  1971.          if (wp->visible  &&  wp != window) {
  1972.  
  1973.             /*
  1974.              * when we sync a command, we need to use the same assertions
  1975.              *  as those in editor( ).
  1976.              *
  1977.              * if everything is everything, these core asserts are TRUE.
  1978.              */
  1979. #if defined( __MSC__ )
  1980.             assert( wp != NULL );
  1981.             assert( wp->file_info != NULL );
  1982.             assert( wp->file_info->line_list != NULL );
  1983.             assert( wp->file_info->line_list_end != NULL );
  1984.             assert( wp->file_info->line_list_end->len == EOF );
  1985.             assert( wp->visible == TRUE );
  1986.             assert( wp->rline >= 0 );
  1987.             assert( wp->rline <= wp->file_info->length + 1 );
  1988.             assert( wp->rcol >= 0 );
  1989.             assert( wp->rcol < MAX_LINE_LENGTH );
  1990.             assert( wp->ccol >= wp->start_col );
  1991.             assert( wp->ccol <= wp->end_col );
  1992.             assert( wp->bcol >= 0 );
  1993.             assert( wp->bcol < MAX_LINE_LENGTH );
  1994.             assert( wp->bcol == wp->rcol-(wp->ccol - wp->start_col) );
  1995.             assert( wp->start_col >= 0 );
  1996.             assert( wp->start_col < wp->end_col );
  1997.             assert( wp->end_col < g_display.ncols );
  1998.             assert( wp->cline >= wp->top_line );
  1999.             assert( wp->cline <= wp->bottom_line );
  2000.             assert( wp->top_line > 0 );
  2001.             assert( wp->top_line <= wp->bottom_line );
  2002.             assert( wp->bottom_line < MAX_LINES );
  2003.             assert( wp->bin_offset >= 0 );
  2004.             if (wp->ll->next == NULL)
  2005.                assert( wp->ll->len == EOF );
  2006.             else
  2007.                assert( wp->ll->len >= 0 );
  2008.             assert( wp->ll->len <  MAX_LINE_LENGTH );
  2009. #endif
  2010.  
  2011.             (*do_it[g_status.command])( wp );
  2012.             show_line_col( wp );
  2013.             show_ruler_pointer( wp );
  2014.          }
  2015.       }
  2016.       mode.sync_sem = TRUE;
  2017.       for (fp = g_status.file_list; fp != NULL; fp = fp->next)
  2018.          if (fp->dirty != FALSE)
  2019.             fp->dirty = GLOBAL;
  2020.    }
  2021. }
  2022.  
  2023.  
  2024. /*
  2025.  * Name:    editor
  2026.  * Purpose: Set up the editor structures and display changes as needed.
  2027.  * Date:    June 5, 1991
  2028.  * Notes:   Master editor routine.
  2029.  */
  2030. void editor( )
  2031. {
  2032. char *name;  /* name of file to start editing */
  2033. register WINDOW *window;        /* current active window */
  2034. int  c;
  2035.  
  2036.    /*
  2037.     * initialize search and seize
  2038.     */
  2039.    g_status.sas_defined = FALSE;
  2040.    for (c=0; c<SAS_P; c++)
  2041.       g_status.sas_arg_pointers[c] = NULL;
  2042.  
  2043.    g_status.file_mode = TEXT;
  2044.    /*
  2045.     * Check that user specified file to edit, if not offer help
  2046.     */
  2047.    if (g_status.argc > 1) {
  2048.       c = *g_status.argv[1];
  2049.       if (c == '/'  ||  c == '-') {
  2050.          c = *(g_status.argv[1] + 1);
  2051.          if (c == 'f'  ||  c == 'g') {
  2052.             /*
  2053.              * with search and seize their has to be at least 4 arg's, e.g.
  2054.              *    tde -f findme *.c
  2055.              */
  2056.             if (g_status.argc >= 4) {
  2057.  
  2058.                assert( strlen( g_status.argv[2] ) < MAX_COLS );
  2059.  
  2060.                if (c == 'f') {
  2061.                   g_status.command = DefineGrep;
  2062.                   strcpy( (char *)sas_bm.pattern, g_status.argv[2] );
  2063.                } else {
  2064.                   g_status.command = DefineRegXGrep;
  2065.                   strcpy( (char *)regx.pattern, g_status.argv[2] );
  2066.                }
  2067.  
  2068.                for (c=3; c <= g_status.argc; c++)
  2069.                   g_status.sas_arg_pointers[c-3] = g_status.argv[c];
  2070.                g_status.sas_argc = g_status.argc - 3;
  2071.                g_status.sas_arg = 0;
  2072.                g_status.sas_argv = g_status.sas_arg_pointers;
  2073.                g_status.sas_found_first = FALSE;
  2074.                if (g_status.command == DefineGrep) {
  2075.                   g_status.sas_defined = TRUE;
  2076.                   g_status.sas_search_type = BOYER_MOORE;
  2077.                   bm.search_defined = sas_bm.search_defined = OK;
  2078.                   build_boyer_array( );
  2079.                   c = OK;
  2080.                } else {
  2081.                   c = build_nfa( );
  2082.                   if (c == OK) {
  2083.                      g_status.sas_defined = TRUE;
  2084.                      g_status.sas_search_type = REG_EXPRESSION;
  2085.                      regx.search_defined = sas_regx.search_defined = OK;
  2086.                   } else
  2087.                      g_status.sas_defined = FALSE;
  2088.                }
  2089.                if (c != ERROR)
  2090.                   c = search_and_seize( g_status.current_window );
  2091.             } else
  2092.                c = ERROR;
  2093.          } else if (c == 'b' || c == 'B') {
  2094.             c = atoi( g_status.argv[1] + 2 );
  2095.             if (c <= 0 || c >= MAX_LINE_LENGTH)
  2096.                c = DEFAULT_BIN_LENGTH;
  2097.             ++g_status.arg;
  2098.             g_status.file_mode = BINARY;
  2099.             g_status.file_chunk = c;
  2100.             c = edit_next_file( g_status.current_window );
  2101.          } else
  2102.             c = ERROR;
  2103.       } else
  2104.          c = edit_next_file( g_status.current_window );
  2105.    } else {
  2106.       name = g_status.rw_name;
  2107.       *name = '\0';
  2108.       /*
  2109.        * file name to edit
  2110.        */
  2111.       c = get_name( ed15, g_display.nlines, name, g_display.text_color );
  2112.  
  2113.       assert( strlen( name ) < MAX_COLS );
  2114.  
  2115.       if (c == OK) {
  2116.          if (*name != '\0')
  2117.             c = attempt_edit_display( name, GLOBAL, TEXT, 0 );
  2118.          else
  2119.             c = dir_help( (WINDOW *)NULL );
  2120.       }
  2121.    }
  2122.  
  2123.    g_status.stop =   c == OK  ?  FALSE  :  TRUE;
  2124.    if (c == OK)
  2125.       set_cursor_size( mode.insert ? g_display.insert_cursor :
  2126.                        g_display.overw_cursor );
  2127.  
  2128.    /*
  2129.     * main loop - keep updating the display and processing any commands
  2130.     *  while user has not pressed the stop key
  2131.     */
  2132.    for (; g_status.stop != TRUE;) {
  2133.       window = g_status.current_window;
  2134.  
  2135.  
  2136.       /*
  2137.        * before we do any editor commands, we start out with some basic
  2138.        *   assumptions.
  2139.        *
  2140.        * if everything is everything, these core asserts are TRUE.
  2141.        */
  2142.       assert( window != NULL );
  2143.       assert( window->file_info != NULL );
  2144.       assert( window->file_info->line_list != NULL );
  2145.       assert( window->file_info->line_list_end != NULL );
  2146.       assert( window->file_info->line_list_end->len == EOF );
  2147.       assert( window->visible == TRUE );
  2148.       assert( window->rline >= 0 );
  2149.       assert( window->rline <= window->file_info->length + 1 );
  2150.       assert( window->rcol >= 0 );
  2151.       assert( window->rcol < MAX_LINE_LENGTH );
  2152.       assert( window->ccol >= window->start_col );
  2153.       assert( window->ccol <= window->end_col );
  2154.       assert( window->bcol >= 0 );
  2155.       assert( window->bcol < MAX_LINE_LENGTH );
  2156.       assert( window->bcol == window->rcol-(window->ccol - window->start_col) );
  2157.       assert( window->start_col >= 0 );
  2158.       assert( window->start_col < window->end_col );
  2159.       assert( window->end_col < g_display.ncols );
  2160.       assert( window->cline >= window->top_line );
  2161.       assert( window->cline <= window->bottom_line );
  2162.       assert( window->top_line > 0 );
  2163.       assert( window->top_line <= window->bottom_line );
  2164.       assert( window->bottom_line < MAX_LINES );
  2165.       assert( window->bin_offset >= 0 );
  2166.       if (window->ll->next == NULL)
  2167.          assert( window->ll->len == EOF );
  2168.       else
  2169.          assert( window->ll->len >= 0 );
  2170.       assert( window->ll->len <  MAX_LINE_LENGTH );
  2171.  
  2172.       display_dirty_windows( window );
  2173.  
  2174.       /*
  2175.        * set the critical error handler flag to a known state before we
  2176.        *   do each editor command.
  2177.        */
  2178.       ceh.flag = OK;
  2179.  
  2180. /*
  2181.  * this code is used during testing to check the amount of memory
  2182.  *    in the near heap.
  2183.  *
  2184.  *  ultoa( _fmsize( window->ll ), buff, 10 );
  2185.  *  s_output( "s=       ", g_display.mode_line, 15, g_display.mode_color );
  2186.  *  s_output( buff, g_display.mode_line, 17, g_display.mode_color );
  2187.  */
  2188.  
  2189.       /*
  2190.        * Get a key from the user.  Look up the function assigned to that key.
  2191.        * All regular text keys are assigned to function 0.  Text characters
  2192.        * are less than 0x100, decimal 256, which includes the ASCII and
  2193.        * extended ASCII character set.
  2194.        */
  2195.       g_status.key_pressed = getkey( );
  2196.       g_status.command = getfunc( g_status.key_pressed );
  2197.       if (g_status.wrapped  ||  g_status.key_pending) {
  2198.          g_status.key_pending = FALSE;
  2199.          g_status.wrapped = FALSE;
  2200.          show_search_message( CLR_SEARCH, g_display.mode_color );
  2201.       }
  2202.       g_status.control_break = FALSE;
  2203.       if (g_status.command >= 0 && g_status.command < NUM_FUNCS) {
  2204.          record_keys( window->bottom_line );
  2205.          (*do_it[g_status.command])( window );
  2206.       }
  2207.    }
  2208.    cls( );
  2209.    xygoto( 0, 0 );
  2210. }
  2211.  
  2212.  
  2213. /*
  2214.  * Name:    display_dirty_windows
  2215.  * Purpose: Set up the editor structures and display changes as needed.
  2216.  * Date:    June 5, 1991
  2217.  * Notes:   Display all windows with dirty files.
  2218.  */
  2219. void display_dirty_windows( WINDOW *window )
  2220. {
  2221. register WINDOW *below;         /* window below current */
  2222. register WINDOW *above;         /* window above current */
  2223. file_infos *file;               /* temporary file structure */
  2224.  
  2225.    /*
  2226.     * update all windows that point to any file that has been changed
  2227.     */
  2228.    above = below = window;
  2229.    while (above->prev || below->next) {
  2230.       if (above->prev) {
  2231.          above = above->prev;
  2232.          show_dirty_window( above );
  2233.       }
  2234.       if (below->next) {
  2235.          below = below->next;
  2236.          show_dirty_window( below );
  2237.       }
  2238.    }
  2239.    file = window->file_info;
  2240.    if (file->dirty == LOCAL || file->dirty == GLOBAL)
  2241.       display_current_window( window );
  2242.    for (file=g_status.file_list; file != NULL; file=file->next)
  2243.       file->dirty = FALSE;
  2244.  
  2245.    /*
  2246.     * Set the cursor position at window->ccol, window->cline.  Show the
  2247.     * user where in the file the cursor is positioned.
  2248.     */
  2249.    xygoto( window->ccol, window->cline );
  2250.    show_line_col( window );
  2251.    show_ruler_pointer( window );
  2252. }
  2253.  
  2254.  
  2255.  
  2256. /*
  2257.  * Name:    show_dirty_window
  2258.  * Purpose: show changes in non-current window
  2259.  * Date:    June 5, 1991
  2260.  * Passed:  window:  pointer to current window
  2261.  */
  2262. void show_dirty_window( WINDOW *window )
  2263. {
  2264. register WINDOW *win;   /* register window pointer */
  2265. int  dirty;
  2266.  
  2267.   win = window;
  2268.   if (win->visible) {
  2269.      dirty = win->file_info->dirty;
  2270.      if (dirty == GLOBAL || dirty == NOT_LOCAL) {
  2271.         display_current_window( win );
  2272.         show_size( win );
  2273.      }
  2274.      show_asterisk( win );
  2275.   }
  2276. }
  2277.